package com.lyun.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lyun.bo.GetAllMovementBo;
import com.lyun.common.EveryConstant;
import com.lyun.common.MyBusinessException;
import com.lyun.common.RedisGeoUtils;
import com.lyun.constants.SplitConstants;
import com.lyun.dto.AdminMovementsDTO;
import com.lyun.mapper.MovementsMapper;
import com.lyun.pojo.Movements;
import com.lyun.pojo.User;
import com.lyun.properties.OssProperties;
import com.lyun.service.CommentService;
import com.lyun.service.MovementLikeService;
import com.lyun.service.MovementsService;
import com.lyun.service.UserService;
import com.lyun.util.MyStrUtils;
import com.lyun.util.RedisUtil;
import com.lyun.vo.AdminMovementsVO;
import com.lyun.vo.MovementsVo;
import com.lyun.vo.PageVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 动态-相关实现
 */
@DubboService
@Slf4j
public class MovementsServiceImpl extends ServiceImpl<MovementsMapper, Movements> implements MovementsService {

    @Resource
    private OssProperties ossProperties;

    @Resource
    private MovementsMapper movementsMapper;

    @DubboReference
    private UserService userService;

    @DubboReference
    private CommentService commentService;

    @Resource
    private MovementLikeService movementLikeService;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    private RedisGeoUtils redisGeoUtils;

    @Resource
    private MyStrUtils myStrUtils;

    @Resource
    private RedisUtil redisUtil;

    private final String OSS_HEADER = "https://zsh-tanhua.oss-cn-wulanchabu.aliyuncs.com//%s";

    @Override
    public PageVO<MovementsVo> selectMovenmentsPageByUserId(GetAllMovementBo bo) {
        Long userId = bo.getUserId();
        Page<Movements> movementsPage = new Page<>(bo.getPage(), bo.getPageSize());
        LambdaQueryWrapper<Movements> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Movements::getUserId, userId);
        queryWrapper.orderByDesc(Movements::getCreateTime);
        movementsMapper.selectPage(movementsPage, queryWrapper);

        List<Movements> movementsList = movementsPage.getRecords();

        if (CollectionUtils.isEmpty(movementsList)) {
            return new PageVO<>();
        }

        User user = userService.getById(userId);
        String tags = user.getTags();
        List<String> tagList = new ArrayList<>();
        if (StringUtils.hasText(tags)) {
            tagList = myStrUtils.strToList(tags, SplitConstants.SPLIT_STR);
        }

        //List<Long> movementIdList = movementsList.stream().map(Movements::getId).collect(Collectors.toList());

        List<String> finalTagList = tagList;
        List<MovementsVo> movementsVoList = movementsList.stream().map(item -> {
            MovementsVo vo = new MovementsVo();
            BeanUtils.copyProperties(item, vo);
            vo.setId(item.getId().toString());
            vo.setAvatar(user.getHeader());
            vo.setNickname(user.getNickname());
            vo.setGender(user.getGender());
            vo.setAge(user.getAge());
            vo.setTags(finalTagList);
            String imageContent = item.getImageContent();
            if (StringUtils.hasText(imageContent)) {
                List<String> imageContentList = myStrUtils.strToListWithPrefix(imageContent, SplitConstants.SPLIT_STR, ossProperties.getUrlHeader());
                vo.setImageContent(imageContentList);
            }

            vo.setCreateDate(item.getCreateTime().toString());

            //todo 是否点赞和喜欢
            vo.setHasLiked(movementLikeService.isLiked(item.getId(), userId));
            vo.setHasLoved(movementLikeService.isLoved(item.getId(), userId));

            return vo;
        }).collect(Collectors.toList());

        PageVO<MovementsVo> vo = new PageVO<>();
        vo.setCounts(movementsPage.getTotal());
        vo.setPages(movementsPage.getPages());
        vo.setPage(bo.getPage());
        vo.setPageSize(bo.getPageSize());
        vo.setItems(movementsVoList);

        return vo;
    }

    @Override
    public Long saveAndGetId(Movements movements) {
        movementsMapper.insert(movements);
        return movements.getId();
    }

    @Override
    public List<Movements> queryByIds(List<Long> ids) {
        return movementsMapper.selectByIds(ids);
    }

    @Override
    public MovementsVo getMovementInfoByMovementId(Long movementId, Long selfId) {
        MovementsVo vo = new MovementsVo();
        Movements movements = movementsMapper.selectById(movementId);
        if (movements == null) {
            return vo;
        }
        User user = userService.getById(movements.getUserId());
        if (user == null) {
            return vo;
        }
        BeanUtils.copyProperties(movements, vo);
        vo.setCreateDate(movements.getCreateTime().toString());
        vo.setId(movements.getId().toString());
        vo.setAvatar(user.getHeader());
        vo.setNickname(user.getNickname());
        vo.setGender(user.getGender());
        vo.setAge(user.getAge());
        if (StringUtils.hasText(user.getTags())) {
            vo.setTags(myStrUtils.strToList(user.getTags(), SplitConstants.SPLIT_STR));
        }
        List<String> imageList = new ArrayList<>();
        if (StringUtils.hasText(movements.getImageContent())) {
            String[] splitArray = movements.getImageContent().split(SplitConstants.SPLIT_STR);
            for (String s : splitArray) {
                imageList.add(String.format(OSS_HEADER, s));
            }
        }
        vo.setImageContent(imageList);
        return addParametersWithUserId(vo, selfId);
    }

    /**
     * 查询推荐动态
     * 修改后
     *
     * @param id       用户id
     * @param page
     * @param pagesize
     * @return
     */
    //todo 重写该接口
    @Override
    public PageVO<MovementsVo> getRecommendFriendList(Long id, Integer page, Integer pagesize) {
//        return new PageVo<>();
        PageVO<MovementsVo> vo = new PageVO<>();
        vo.setPage(page);

        // 1.先获取自己位置
        List<Point> position = redisTemplate.opsForGeo().position(EveryConstant.REDIS_GEO_USER, id.toString());

        // 2.获取附近动态List
        List<Movements> movementsList = new ArrayList<>();
        //  2.1如果没有自己位置,或周围没有动态,就随便查
        if (CollectionUtils.isEmpty(position) || position.get(0) == null) {
            log.info("{}: no location", id);
            //1,先随便获取几个动态
            //2.根据动态对应的userId再查询用户信息
            Page<Movements> movementsPage = new Page<>(page, pagesize);
            LambdaQueryWrapper<Movements> movementsLambdaQueryWrapper = new LambdaQueryWrapper<>();
            movementsLambdaQueryWrapper.ne(Movements::getUserId, id);
            movementsLambdaQueryWrapper.last("order by rand()");
            movementsMapper.selectPage(movementsPage, movementsLambdaQueryWrapper);
            movementsList = movementsPage.getRecords();
//            List<MovementsVo> recommendFriendListByRand = movementsMapper.getRecommendFriendListByRand(pagesize);
        } else {
            //  2.2如果有自己位置,就先查周边的动态。再根据数量，不够就去数据库补充一部分。
            //通过redisGeo 获取 周边5公里的动态 这个方法的返回值 已经是排序过了的。距离从小到大
            Circle circle = new Circle(position.get(0), new Distance(5, Metrics.KILOMETERS));

            GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = redisTemplate.opsForGeo().radius(EveryConstant.REDIS_GEO_MOVEMENTS, circle);

            if (geoResults == null || geoResults.getContent().isEmpty()) {
                //周围没有动态 也是随便获取几个动态给他
                Page<Movements> movementsPage = new Page<>(page, pagesize);
                LambdaQueryWrapper<Movements> movementsLambdaQueryWrapper = new LambdaQueryWrapper<>();
                movementsLambdaQueryWrapper.ne(Movements::getUserId, id);
                movementsLambdaQueryWrapper.last("order by rand()");
                movementsMapper.selectPage(movementsPage, movementsLambdaQueryWrapper);
                movementsList.addAll(movementsPage.getRecords());
            } else {
                //将获取到附近的动态 idList
                List<Long> ids = new ArrayList<>();
                for (GeoResult<RedisGeoCommands.GeoLocation<String>> geoResult : geoResults) {
                    Long movementId = Long.parseLong(geoResult.getContent().getName());
                    ids.add(movementId);
                }
                LambdaQueryWrapper<Movements> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.in(Movements::getId, ids);
                List<Movements> resultList = movementsMapper.selectList(queryWrapper);
                movementsList.addAll(resultList);
            }

            //如果附近动态少于10条 那就再去凑到10条
            if (movementsList.size() < pagesize) {
                //随机获取出来少的部分
                LambdaQueryWrapper<Movements> movementsLambdaQueryWrapper = new LambdaQueryWrapper<>();
                movementsLambdaQueryWrapper.last("ORDER BY rand() limit " + (pagesize - movementsList.size()));
                List<Movements> movementsList2 = movementsMapper.selectList(movementsLambdaQueryWrapper);
                //拼上
                movementsList.addAll(movementsList2);
            }
        }

        // 3.查询出来之后的结果,抽取出userId,调用userService去获取user的信息
        List<Long> userIdList = movementsList.stream().map(Movements::getUserId).collect(Collectors.toList());
        List<User> userList = userService.getListByIds(userIdList);
        Map<Long, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));

        // 4.拼接信息,生成vo
        List<MovementsVo> voList = movementsList.stream().map(item -> {
            User user = userMap.get(item.getUserId());
            MovementsVo movementsVo = new MovementsVo();
            BeanUtils.copyProperties(item, movementsVo);
            movementsVo.setId(item.getId().toString());

            movementsVo.setCreateDate(item.getCreateTime().toString());
            if (user == null) {
                return null;
            }
            if (StringUtils.hasText(user.getTags())) {
                String[] split = user.getTags().split(SplitConstants.SPLIT_STR);
                movementsVo.setTags(new ArrayList<>(Arrays.asList(split)));
            }
            String imageContent = item.getImageContent();
            if (StringUtils.hasText(imageContent)) {
                String[] split = imageContent.split(SplitConstants.SPLIT_STR);
                String urlHeader = ossProperties.getUrlHeader();
                List<String> imageList = new ArrayList<>();
                for (String s : split) {
                    String imgUrl = urlHeader + s;
                    imageList.add(imgUrl);
                }
                movementsVo.setImageContent(imageList);
            } else {
                movementsVo.setImageContent(Collections.emptyList());
            }
            movementsVo.setAvatar(user.getHeader());
            movementsVo.setNickname(user.getNickname());
            movementsVo.setGender(user.getGender());
            movementsVo.setAge(user.getAge());
            return addParametersWithUserId(movementsVo, id);
        }).filter(Objects::nonNull).collect(Collectors.toList());

        PageVO<MovementsVo> pageVo = new PageVO<>();
        pageVo.setPage(page);
        pageVo.setPageSize(pagesize);
        pageVo.setItems(voList);
        pageVo.setCounts((long) voList.size());

        return pageVo;
    }

    /**
     * 分页查询动态列表
     */
    @Override
    public PageVO<AdminMovementsVO> getMovementPage(AdminMovementsDTO dto) {
        Page<Movements> movementsPage = new Page<>(dto.getPage(), dto.getPageSize());
        LambdaQueryWrapper<Movements> queryWrapper = Wrappers.<Movements>lambdaQuery()
                .eq(dto.getStatus() != null, Movements::getStatus, dto.getStatus())
                .like(StringUtils.hasText(dto.getName()), Movements::getTextContent, dto.getName());

        movementsMapper.selectPage(movementsPage, queryWrapper);

        List<Movements> movementsList = movementsPage.getRecords();
        List<Long> userIdList = movementsList.stream().map(Movements::getUserId).collect(Collectors.toList());
        List<User> userList = userService.getListByIds(userIdList);
        Map<Long, String> userMap = userList.stream().collect(Collectors.toMap(User::getId, User::getHeader));

        List<AdminMovementsVO> voList = movementsList.stream().map(item -> {
            AdminMovementsVO vo = new AdminMovementsVO();
            BeanUtils.copyProperties(item, vo);
            vo.setId(item.getId().toString());
            vo.setUserId(item.getUserId().toString());
            List<String> imgList = new ArrayList<>();
            String imageContent = item.getImageContent();
            if (userMap.containsKey(item.getUserId())) {
                vo.setHeader(userMap.get(item.getUserId()));
            }
            if (StringUtils.hasText(imageContent)) {
                String[] split = imageContent.split(SplitConstants.SPLIT_STR);
                for (String s : split) {
                    imgList.add(String.format(OSS_HEADER, s));
                }
            }
            vo.setImageContent(imgList);
            return vo;
        }).collect(Collectors.toList());

        PageVO<AdminMovementsVO> vo = new PageVO<>();
        vo.setCounts(movementsPage.getTotal());
        vo.setItems(voList);
        return vo;
    }

    /**
     * 修改动态状态
     *
     * @param dto
     */
    @Override
    public void auditMovements(AdminMovementsDTO dto) {
        if (!StringUtils.hasText(dto.getMovementsId())) {
            throw new MyBusinessException("请输入id");
        }
        Movements movements = movementsMapper.selectById(Long.valueOf(dto.getMovementsId()));
        if (movements == null) {
            throw new MyBusinessException("动态为空");
        }
        Integer status = dto.getStatus();
        movements.setStatus(status);
        movementsMapper.updateById(movements);
        if (status == 1) {
            redisTemplate.opsForGeo().add(EveryConstant.REDIS_GEO_MOVEMENTS, new Point(movements.getLongitude(), movements.getLatitude()), dto.getMovementsId());
        }
    }

    /**
     * 查询推荐动态
     * 修改前
     * @param id       用户id
     * @param page
     * @param pagesize
     * @return
     */
    /*public PageVo<MovementsVo> getRecommendFriendList(Long id, Integer page, Integer pagesize) {
        PageVo<MovementsVo> vo = new PageVo<>();
        vo.setPageSize(pagesize);
        vo.setPage(page);

        // 1.先获取自己位置
        List<Point> position = redisTemplate.opsForGeo().position(EveryConstant.REDIS_GEO_USER, id.toString());

        if (position.size() == 0) {

            //没有自己的定位 随便获取几个动态给他
            List<MovementsVo> recommendFriendListByRand = movementsMapper.getRecommendFriendListByRand(pagesize);

            vo.setCounts((long) recommendFriendListByRand.size());
            vo.setPages((long) Math.ceil((double) recommendFriendListByRand.size() / pagesize));

            //随机获取的列表 有几个参数没写入
            // 调用addParametersWithUserId方法补齐参数
            recommendFriendListByRand = recommendFriendListByRand.stream().peek((movementsVo) -> {
                movementsVo = addParametersWithUserId(movementsVo, id);
            }).collect(Collectors.toList());

            vo.setItems(recommendFriendListByRand);
            return vo;
        }

        Point point = position.get(0);
        // 通过redisGeo 获取 周边5公里的动态 这个方法的返回值 已经是排序过了的。距离从小到大
        Circle circle = new Circle(point, new Distance(5, Metrics.KILOMETERS));

        GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResults = redisTemplate.opsForGeo().radius(EveryConstant.REDIS_GEO_MOVEMENTS, circle);

        if (geoResults == null) {
            //周围没有动态 也是随便获取几个动态给他
            List<MovementsVo> recommendFriendListByRand = movementsMapper.getRecommendFriendListByRand(pagesize);
            vo.setCounts((long) recommendFriendListByRand.size());

            vo.setPages((long) Math.ceil((double) recommendFriendListByRand.size() / pagesize));

            recommendFriendListByRand = recommendFriendListByRand.stream().peek((movementsVo) -> {
                movementsVo = addParametersWithUserId(movementsVo, id);
            }).collect(Collectors.toList());

            vo.setItems(recommendFriendListByRand);
            return vo;
        }

        //将动态id和距离 存入map
        List<Long> ids = new ArrayList<>();
        Map<Long, Double> distanceMap = new HashMap<>();
        for (GeoResult<RedisGeoCommands.GeoLocation<Object>> geoResult : geoResults) {
            Long movementId = Long.parseLong((String) geoResult.getContent().getName());
            Distance distance = geoResult.getDistance();

            ids.add(movementId);
            distanceMap.put(movementId, distance.getValue());
        }

        List<Movements> movements = this.listByIds(ids);

        List<MovementsVo> listVo = movements.stream().map((movement) -> {
            MovementsVo movementsVo = new MovementsVo();
            BeanUtils.copyProperties(movement, movementsVo);
            movementsVo.setId(movement.getId().toString());
            User user = userService.getById(movement.getUserId());
            movementsVo.setAge(user.getAge());
            movementsVo.setAvatar(user.getHeader());
            String tags = user.getTags();
            String[] split = tags.split(", ");
            List<String> tagsList = new ArrayList<>();
            for (String s : split) {
                tagsList.add(s);
            }

            movementsVo.setTags(tagsList);
            List<String> imageList = new ArrayList<>();
            String imageContent = movement.getImageContent();
            if (imageContent != null) {
                String[] imageSplit = imageContent.split(",");
                for (String s : imageSplit) {
                    imageList.add(s);
                }
            }

            movementsVo.setImageContent(imageList);
            movementsVo.setDistance(distanceMap.get(movement.getId()) + "m");

            movementsVo.setCreateDate(movement.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

            //计算评论数
            int commentCount = commentService.getCommentCount(movement.getId());
            //计算点赞数
            int likeCount = movementLikeService.getLikeCount(movement.getId());
            //计算喜欢数
            int loveCount = movementLikeService.getLoveCount(movement.getId());

            movementsVo.setLikeCount(likeCount);
            movementsVo.setCommentCount(commentCount);
            movementsVo.setLoveCount(loveCount);

            movementsVo.setHasLiked(movementLikeService.isLiked(movement.getId(), id));
            movementsVo.setHasLoved(movementLikeService.isLoved(movement.getId(), id));

            return movementsVo;
        }).collect(Collectors.toList());

        if (listVo.size() < 10) {
            //如果附近动态少于10条 那就再去凑到10条
            List<MovementsVo> recommendFriendListByRand = movementsMapper.getRecommendFriendListByRand(10 - movements.size());
            recommendFriendListByRand = recommendFriendListByRand.stream().map((movementsVo) -> {
                return addParametersWithUserId(movementsVo, id);
            }).collect(Collectors.toList());

            listVo.addAll(recommendFriendListByRand);
        }

        PageVo<MovementsVo> pageVo = new PageVo<>();
        pageVo.setItems(listVo);
        pageVo.setCounts((long) listVo.size());

        return pageVo;
    }*/

    /**
     * 根据id 给vo加上对应的参数 比如loveCount likeCount 等等
     * 同时把vo的imageList也加上URL
     *
     * @param movementsVo
     * @param id          当前用户的id
     * @return
     */
    public MovementsVo addParametersWithUserId(MovementsVo movementsVo, Long id) {

        movementsVo.setHasLiked(movementLikeService.isLiked(Long.valueOf(movementsVo.getId()), id));
        movementsVo.setHasLoved(movementLikeService.isLoved(Long.valueOf(movementsVo.getId()), id));

        List<Point> position = redisTemplate.opsForGeo().position(EveryConstant.REDIS_GEO_MOVEMENTS, movementsVo.getId());
        List<Point> position2 = redisTemplate.opsForGeo().position(EveryConstant.REDIS_GEO_USER, id.toString());

        if (CollectionUtils.isEmpty(position) ||
                CollectionUtils.isEmpty(position2) ||
                position.size() == 0 || position2.size() == 0 ||
                position2.get(0) == null || position.get(0) == null) {
            movementsVo.setDistance(" 未知。。");
            return movementsVo;
        }

        String distance = RedisGeoUtils.getDistance(position2.get(0), position.get(0));
        movementsVo.setDistance(distance);

        return movementsVo;
    }

}
